package cn.yzgy.yzgyoa.ucenter.controller;


import cn.yzgy.yzgyoa.common.annotation.ApiDesc;
import cn.yzgy.yzgyoa.common.annotation.Syslog;
import cn.yzgy.yzgyoa.common.constant.YzCacheName;
import cn.yzgy.yzgyoa.common.crsp.StatusCode;
import cn.yzgy.yzgyoa.common.crsp.Urmf;
import cn.yzgy.yzgyoa.common.exception.SystemException;
import cn.yzgy.yzgyoa.common.shiro.AuthRealm;
import cn.yzgy.yzgyoa.common.utils.*;
import cn.yzgy.yzgyoa.ucenter.dto.LoginInfoDTO;
import cn.yzgy.yzgyoa.ucenter.dto.LoginOkDTO;
import cn.yzgy.yzgyoa.ucenter.dto.OnlineUserDto;
import cn.yzgy.yzgyoa.ucenter.dto.UserDTO;
import cn.yzgy.yzgyoa.ucenter.entity.SysUser;
import cn.yzgy.yzgyoa.ucenter.entity.SysUserDetail;
import cn.yzgy.yzgyoa.ucenter.entity.SysUserRole;
import cn.yzgy.yzgyoa.ucenter.service.ISysOrganizationService;
import cn.yzgy.yzgyoa.ucenter.service.ISysUserDetailService;
import cn.yzgy.yzgyoa.ucenter.service.ISysUserRoleService;
import cn.yzgy.yzgyoa.ucenter.service.ISysUserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.yzgy.yzgyoa.ucenter.service.impl.SysOnlineUserService;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import cn.yzgy.yzgyoa.common.controller.BaseController;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author Yqsen
 * @since 2019-08-20
 */
@Slf4j
@Syslog
@RestController
@RequestMapping("/ucenter")
public class SysUserController extends BaseController {

    private final YzCrypto yzCrypto;
    private final AuthRealm authRealm;
    private final ISysUserDetailService sysUserDetailService;
    private final ISysOrganizationService sysOrganizationService;
    private final ISysUserService iSysUserService;
    private final ISysUserRoleService iSysUserRoleService;
    private final SysOnlineUserService sysOnlineUserService;
    private final YzCacheHelper yzCacheHelper;


    public SysUserController(YzCrypto yzCrypto, ISysUserDetailService sysUserDetailService,
                             AuthRealm authRealm, ISysOrganizationService sysOrganizationService, ISysUserService iSysUserService,
                             ISysUserRoleService iSysUserRoleService, SysOnlineUserService sysOnlineUserService,
                             YzCacheHelper yzCacheHelper) {
        this.authRealm = authRealm;
        this.sysUserDetailService = sysUserDetailService;
        this.yzCrypto = yzCrypto;
        this.sysOrganizationService = sysOrganizationService;
        this.sysOnlineUserService = sysOnlineUserService;
        this.iSysUserService = iSysUserService;
        this.iSysUserRoleService = iSysUserRoleService;
        this.yzCacheHelper = yzCacheHelper;
    }

    @PostMapping("/login")
    @ApiDesc(value = "用户登录", log = true)
    public Object login(@Validated @RequestBody LoginInfoDTO loginInfo) throws NullPointerException {
        log.info("用户登录：userName = {} psw = {}", loginInfo.getUserName(), loginInfo.getPsw());
        //密码处理
        loginInfo.setPsw(yzCrypto.getShaPassword(loginInfo.getPsw()));
        UsernamePasswordToken token = new UsernamePasswordToken(loginInfo.getUserName(), loginInfo.getPsw());

        try {
            getSubject().login(token);
        } catch (UnknownAccountException e) {
            throw new SystemException(StatusCode.USER_NOT_EXIST);
        } catch (IncorrectCredentialsException e) {
            throw new SystemException(StatusCode.USER_WRONG_PASSWORD);
        } catch (LockedAccountException e) {
            throw new SystemException(StatusCode.USER_FROZEN);
        } catch (AuthenticationException e) {
            throw new SystemException(StatusCode.USER_AUTHENTICATION_EXCEPTION);
        } catch (NullPointerException e) {
            throw new SystemException(StatusCode.EXCEPTION, e.getMessage());
        }

        // 踢掉现在已经在线的用户
        sysOnlineUserService.kickUser(loginInfo.getUserName(),getSession().getId());
        log.info("用户登录 用户名：{} 时间：{} IP:{} ", loginInfo.getUserName(), TimeUtil.getCurrentTimeString(), IpUtil.getIpAddr(getRequest()));

        // 组装返回消息
        SysUser sysUser = (SysUser) getSubject().getPrincipal();

        LoginOkDTO loginOkDTO = new LoginOkDTO()
                .setUser(sysUserDetailService.getUserDetailInfoByUid(sysUser.getId()))
                .setToken(getSubject().getSession().getId().toString())
                .setPerms(authRealm.getPermissions());

        return Urmf.success(getCurrentMethodName(), loginOkDTO);
    }

    @GetMapping("info")
    @ApiDesc("获取Subject")
    public Object getSubject1(){
        return Urmf.success("获取Subject", getSubject().getPrincipal());
    }

    @GetMapping("online_user")
    @ApiDesc("获取在线用户")
    @RequiresPermissions("api:ucenter:online_user:get")
    public Urmf getOnlineUser() {
        List<OnlineUserDto> onlineUserDtoList = sysOnlineUserService.getOnlineUsers();
        Page<OnlineUserDto> onlineUserDtoPage = new Page<OnlineUserDto>(1, 50)
                .setCurrent(1)
                .setSize(onlineUserDtoList.size())
                .setTotal(onlineUserDtoList.size())
                .setRecords(onlineUserDtoList);
        return Urmf.success(getCurrentMethodName(), onlineUserDtoPage);
    }

    @GetMapping("/online_user_num")
    @ApiDesc("获取在线用户数量")
    @RequiresPermissions("api:ucenter:online_user:num")
    public Urmf getCountUserOnlineNum() {
        List<OnlineUserDto> onlineUserDtoList = sysOnlineUserService.getOnlineUsers();
        int onlineNum = onlineUserDtoList.size();
        return Urmf.success(getCurrentMethodName(), onlineNum);
    }

    @PostMapping("kick")
    @ApiDesc(value = "强制用户下线", log = true)
    @RequiresPermissions("api:ucenter:online_user:kick")
    public Urmf kickUser(@RequestBody JSONObject requestBody) {
        return Urmf.success(getCurrentMethodName(), sysOnlineUserService.kickUser(requestBody.getString("loginName")));
    }

    /**
     * 分页获取user列表数据
     * @param page 数据页
     * @param size 数据条数
     * @return 数据页
     */
    @ApiDesc(value = "获取用户列表数据",log = true)
    @GetMapping("/user/queryAllUser")
    @RequiresPermissions("api:user:data:get")
    public Object queryAllUser(String page, String size, HttpServletRequest request) {
        QueryWrapper<UserDTO> wrapper = new QueryWrapper<>();
        String organization= request.getParameter("organization");
        if(organization!=null&&!"".equals(organization)){
            organization = sysOrganizationService.getEnAbbr(organization);
            wrapper=wrapper.likeRight("dataPerm",organization);
        }
        else {
            SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
            String dataPerm=sysUserDetailService.dataPerm(user.getId());
            wrapper=wrapper.likeRight("dataPerm",dataPerm);
        }
        //组装Mybatis-Plus 分页查询结构，将条件放入QueryWarpper
        IPage<UserDTO> ipage = new Page<>(Integer.parseInt(page), Integer.parseInt(size));
        Integer num = (Integer.parseInt(page)-1)*Integer.parseInt(size);
        String total = iSysUserService.countData();
        IPage userIpages = iSysUserService.queryAllUserDTO(wrapper,ipage,num, Integer.parseInt(size));
        userIpages.setTotal(Long.parseLong(total));
        return Urmf.success("获取User数据列表", StatusCode.SUCCESS, userIpages);
    }

    @ApiDesc(value = "重置用户密码",log = true)
    @PutMapping("/user/resetPwd")
    @RequiresPermissions("api:user:data:resetpwd")
    public Object resetPwd(@RequestBody Map<String, String> userId){
        //md5(md5(data.field.pwd) + "scgngms0.01") 加密123456之后的字符串
        String restPwd = "93a813f40029dc211b0d0d522588aeea";
        String salt=String.valueOf(SnowflakeIdWorker.getNext());
        //对密码进行加密
        String psw= yzCrypto.getEncodedPassword(restPwd,salt);

        int num = iSysUserService.resetPwd(psw, salt, userId.get("userId"));
        if(num>0){
            return Urmf.success("重置用户密码", StatusCode.SUCCESS, "重置密码成功！");
        }
        else{
            return Urmf.fail("重置用户密码", StatusCode.FAILED, "重置密码失败！");
        }
    }

    @ApiDesc("用户修改密码")
    @PutMapping("/user/updatePwd")
    @RequiresPermissions("api:user:data:updatepwd")
    public Object updatePwd(@RequestBody Map<String, String> pwdInfo){
        String oldPwd = pwdInfo.get("oldpwd");
        String newPwd = pwdInfo.get("newpwd");
        String realNewPwd = pwdInfo.get("realnewpwd");

        if(!newPwd.equals(realNewPwd)){
            return Urmf.fail("用户修改密码", StatusCode.FAILED, "两次密码不一致！");
        }
        SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
        SysUser userInfo = iSysUserService.getById(user.getId());
        String psw= yzCrypto.getEncodedPassword(oldPwd,userInfo.getSalt());

        if(!userInfo.getPsw().equals(psw)){
            return Urmf.fail("用户修改密码", StatusCode.FAILED, "原密码错误！");
        }
        String salt=String.valueOf(SnowflakeIdWorker.getNext());
        //对密码进行加密
        psw= yzCrypto.getEncodedPassword(newPwd,salt);

        int num = iSysUserService.resetPwd(psw, salt, userInfo.getId().toString());
        if(num>0){
            return Urmf.success("用户修改密码", StatusCode.SUCCESS, "修改成功！");
        }
        else{
            return Urmf.fail("用户修改密码", StatusCode.FAILED, "修改密码失败！");
        }
    }

    /**
     * 根据前端传入对象修改数据表内容
     * @param userInfo 客户对象
     * @return 成功或失败状态
     */
    @ApiDesc("修改用户信息")
    @PutMapping("/user/changeInfo")
    @RequiresPermissions("api:user:data:changeinfo")
    @Transactional(rollbackFor = Exception.class)
    public Object changeInfo(@RequestBody Map<String, String> userInfo){
        Long uid=Long.parseLong(userInfo.get("hidId"));

        //组装用户详情对象 并插入数据库
        SysUserDetail sysUserDetail=new SysUserDetail();
        sysUserDetail.setUid(uid);
        sysUserDetail.setGender(Integer.parseInt(userInfo.get("gender")));
        sysUserDetail.setName(userInfo.get("name"));
        sysUserDetail.setPhone(userInfo.get("phone"));
        //组织机构key
        String oid = "gid";
        if(userInfo.get(oid) != null && !"".equals(userInfo.get(oid))){
            sysUserDetail.setOid(Long.parseLong(userInfo.get(oid)));
        }
        String dataPerm = sysOrganizationService.getEnAbbr(userInfo.get(oid));
        sysUserDetail.setDataPerm(dataPerm);
        int num = sysUserDetailService.changeInfo(sysUserDetail);
        if(num<=0){
            throw new SystemException(StatusCode.FAILED,"用户详细信息修改失败！");
        }
        //获取角色value
        String roles="roles";
        if (userInfo.get(roles) == null && "".equals(userInfo.get(roles))) {
            throw new SystemException(StatusCode.FAILED,"用户角色不能为空！");
        }
        List<String> result = new LinkedList<>();
        result.add(userInfo.get("hidId"));
        num = iSysUserRoleService.delSysUserRole(result);
        if(num<=0){
            throw new SystemException(StatusCode.FAILED,"处理该用户原角色对应数据失败！");
        }
        SysUserRole sysUserRole=new SysUserRole();
        sysUserRole.setUid(uid);
        String[] rId = userInfo.get("roles").split(",");
        for (String s : rId) {
            sysUserRole.setRoleId(Integer.parseInt(s));
            num += iSysUserRoleService.addSysUserRole(sysUserRole);
        }
        if (num <= 0) {
            throw new SystemException(StatusCode.FAILED,"修改User数据内容！");
        }
        //清除redis菜单缓存
        yzCacheHelper.autoClearCache(num, YzCacheName.MENU_CACHE_NAME);
        //检查redis数据库发现shiro中还存在部分权限缓存需要删除
        yzCacheHelper.autoClearCache(num, "shiro:cache");
        return Urmf.success("修改User数据内容", StatusCode.SUCCESS,"数据内容已改变");
    }

    /**
     * 新增用户表数据
     *
     * @param userInfo 用户相关信息集合
     * @return 成功状态
     */
    @ApiDesc("新增用户表数据")
    @PostMapping("/user/addUser")
    @RequiresPermissions("api:user:data:add")
    @Transactional(rollbackFor = Exception.class)
    public Object addSysRole(@RequestBody Map<String, String> userInfo) {
        SysUser sysUser=new SysUser();
        //获取有序增长随机数SnowflakeIdWorker.getNext()
        Long uid=SnowflakeIdWorker.getNext();
        sysUser.setLoginName(userInfo.get("loginName"));
        if (iSysUserService.check(sysUser) == 1) {
            return Urmf.fail("新增User数据", StatusCode.FAILED, "用户名重复！");
        }
        //组装用户对象 并插入数据库
        sysUser.setId(uid);
        String salt=String.valueOf(SnowflakeIdWorker.getNext());
        //对密码进行加密
        String psw= yzCrypto.getEncodedPassword(userInfo.get("psw"),salt);
        sysUser.setPsw(psw);
        sysUser.setState(Integer.parseInt(userInfo.get("state")));
        sysUser.setSalt(salt);
        int num = iSysUserService.addSysUser(sysUser);

        if(num <= 0){
            throw new SystemException(StatusCode.FAILED, "数据添加失败！");
        }

        //组装用户详情对象 并插入数据库
        SysUserDetail sysUserDetail=new SysUserDetail();
        sysUserDetail.setUid(uid);
        sysUserDetail.setName(userInfo.get("name"));
        sysUserDetail.setOid(Long.parseLong(userInfo.get("oid")));
        sysUserDetail.setGender(Integer.parseInt(userInfo.get("gender")));
        sysUserDetail.setPhone(userInfo.get("phone"));
        String dataPerm = sysOrganizationService.getEnAbbr(userInfo.get("oid"));
        sysUserDetail.setDataPerm(dataPerm);
        num = sysUserDetailService.addSysUserDetail(sysUserDetail);
        if(num <= 0){
            throw new SystemException(StatusCode.FAILED, "用户详情数据添加失败！");
        }
        //组装用户角色对象并插入数据库
        SysUserRole sysUserRole=new SysUserRole();
        sysUserRole.setUid(uid);
        //定义角色值
        String roles = "roles";
        if("".equals(userInfo.get(roles))){
            throw new SystemException(StatusCode.FAILED, "用户角色信息为空！");
        }
        String[] rId= userInfo.get(roles).split(",");
        for (String s : rId) {
            sysUserRole.setRoleId(Integer.parseInt(s));
            num = iSysUserRoleService.addSysUserRole(sysUserRole);
            if(num <= 0){
                throw new SystemException(StatusCode.FAILED, "用户角色数据添加失败！");
            }
        }

        return Urmf.success("新增User数据", StatusCode.SUCCESS, "数据添加成功！");
    }

    /**
     * 根据参数停用或者启用表数据
     * @param userInfo hidId用户表主键  way 数据操作方式
     * @return 成功状态
     */
    @ApiDesc("停用/启用User数据")
    @PutMapping("/user/updateData")
    @RequiresPermissions("api:user:data:updatestate")
    public Object updateData(@RequestBody Map<String, String> userInfo){
        String result = userInfo.get("hidId");
        int num= iSysUserService.updateData(result,userInfo.get("way"));
        if(num <= 0){
            return Urmf.fail("改变User数据状态", StatusCode.FAILED,"操作失败！");
        }
        return Urmf.success("改变User数据状态", StatusCode.SUCCESS,"改变User数据"+num+"条！");

    }

    /**
     * 删除用户表数据
     * @param userId 用户ID
     * @return  成功状态
     */
    @ApiDesc("删除用户表数据")
    @DeleteMapping("/user/del")
    @RequiresPermissions("api:user:data:del")
    @Transactional(rollbackFor = Throwable.class, propagation= Propagation.REQUIRED)
    public  Object delSysUser(@RequestBody Map<String, String> userId){
        List<String> result = Arrays.asList(userId.get("userId").substring(0,userId.get("userId").length()-1).split(","));
        for (String s : result) {
            //管理员账户ID
            String adminId = "615602503764934656";
            if (s.equals(adminId)) {
                throw new SystemException(StatusCode.FAILED,"管理员账户不可删除");
            }
        }
        //删除的数据条数总计
        int num;
        //删除用户基表
        num = iSysUserService.delSysUser(result);
        if(num<=0){
            throw new SystemException(StatusCode.FAILED,"删除用户基表数据失败");
        }
        //删除用户详情表
        num = sysUserDetailService.delSysUserDetail(result);
        if(num<=0){
            throw new SystemException(StatusCode.FAILED,"删除用户详情表数据失败");
        }
        //根据角色Id删除角色权限关系表数据
        num = iSysUserRoleService.delSysUserRole(result);
        if(num<=0){
            throw new SystemException(StatusCode.FAILED,"删除用户角色对应数据失败");
        }
        return Urmf.success("删除User数据", StatusCode.SUCCESS,"已成功删除相关数据！");
    }
}
